package cn.edu.jxau.controller;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;

import cn.edu.jxau.controller.cache.Cache;
import cn.edu.jxau.controller.cache.LFUCache;
import cn.edu.jxau.controller.hander.AjaxResultHander;


/**
 * 作为核心控制器,根据service.xml的name值匹配URL请求调用对应的Service中的业务方法
 * CoreServlet
 * @author zclong 2017年9月7日
 */
@WebServlet("/CoreServlet")
public class CoreServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	//日志信息
	private static Logger logger = LoggerFactory.getLogger(CoreServlet.class);
	
    // 全局编码
	public static String ENCODING = "UTF-8";
	
	// 控制层缓存   K:path V:path 去除.do的service配置
    public static Cache<String, ServiceConfig> serviceConfigCache;
    
    //初始化
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        long start = System.currentTimeMillis();
        // 初始化缓存
        serviceConfigCache = new LFUCache<String, ServiceConfig>(100);
        long end = System.currentTimeMillis();
        logger.debug("核心转发器初始化完成，花费了: {} 毫秒", (end - start));
    }

    //程序入口，核心处理方法
    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  
        setCharacterEncoding(request, response); // 编码处理
        // 将请求映射到目标service上
        ServiceConfig targetService = request2ServiceConfig(request);
        if (null == targetService) {
            notfound(request, response);
            return;
        }
        try {
            // 请求 的所有数据(普通数据,验证码,上传信息)
            Map<String, Object[]> params = getRequestParams(request, targetService); // 处理前台传来的参数
            Map<String, Object> data = new HashMap<String, Object>(); // 存放service层处理后的数据
            
            logger.info("访问了该页面："+targetService.getName()+" data: "+ JSON.toJSONString(params));
            
            // 创建Service对象，控制层与业务层的接口（业务层的入口）
            new Service(targetService, params, data);
            
            // 得到要跳转的页面
            String result = (String) data.get("result");
            
            // 处理结果
            handleResult(request, response, data, targetService.getResultConfig(result));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 将请求映射到ServiceConfig
     * @param request
     * @return
     * @zclong
     * @2017年8月5日
     * @ReturnType: ServiceConfig
     */
    public static ServiceConfig request2ServiceConfig(HttpServletRequest request) {
        
        String path = request.getRequestURI(); // path="/login.do" 得到请求路径     
        ServiceConfig cache = serviceConfigCache.get(path); // 从缓存中查找是否存在该请求
        if (null != cache) {
            logger.debug("cache : " + cache);
            return cache;
        }
        
        // 获取请求名
        String reqName = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf(".")); // reqName=login
        
        // ServiceMapper：根据请求路径去加载配置文件，得到相应的方法，信息
        cache = ServiceMapper.getServiceConfig(reqName);
        serviceConfigCache.put(path, cache); // 加入到缓存
        return cache;
    }

    /**
     * 获取请求的数据
     *
     * @param request
     * @param targetService
     * @return
     * @zclong
     * @2017年8月5日
     * @ReturnType: Map<String,String[]>
     */
    private Map<String, Object[]> getRequestParams(HttpServletRequest request, ServiceConfig targetService) {
        Map<String, Object[]> results = new HashMap<>();
        Map<String, String[]> params = request.getParameterMap(); // 前台传入的普通数据
        if (null != params && 0 != params.size()) {
            results.putAll(params);
        }
        return results;
    }

    /**
     * 处理Service执行后的结果
     *
     * @throws IOException
     * @throws ServletException
     */
    private void handleResult(HttpServletRequest request, HttpServletResponse response, Map<String, Object> data,
                              ServiceResultConfig result) throws IOException, ServletException {
        if (null == result)
            return; // 若完成业务什么也不做，即不用跳转到其他页面，返回业务方法返回null;

        String type = result.getType(); // type= dispatcher表示是转发还是重定向或者返回json信息

        String resultName = result.getName(); // resultName = login 即配置文件里的信息

        String path = result.getLocation(); // path : /index.jsp,与service.xml相对应

        // 登录处理
        if (ServiceResultConfig.LOGIN.equals(resultName)) {
            for (Entry<String, Object> entry : data.entrySet()) {
                request.getSession().setAttribute(entry.getKey(), entry.getValue()); // 设置用户session
            }
        }else if(ServiceResultConfig.EXIT.equals(resultName)) { // 注销
            request.getSession().invalidate(); //销毁session
        }

        // 处理登陆失败验证码问题
        if (ServiceResultConfig.FAILED.equals(resultName)) {
            for (Entry<String, Object> entry : data.entrySet()) {
                request.getSession().setAttribute(entry.getKey(), entry.getValue()); // 设置错误信息session，包括登录失败次数
            }
        }

        // 保存其他业务的所有数据，即将数据传递给要转发的页面
        if (!data.isEmpty()) {
            for (Entry<String, Object> entry : data.entrySet()) {
                request.setAttribute(entry.getKey(), entry.getValue());
            }
        }

        // 转发
        if (ServiceResultConfig.TYPE_DISPATCHER.equals(type)) {
            request.getRequestDispatcher(path).forward(request, response);
            return;
        }
        // 重定向
        if (ServiceResultConfig.TYPE_REDIRECT.equals(type)) {
            response.sendRedirect(request.getContextPath() + result.getLocation());
            return;
        }

        // 异步请求/JSON数据
        if (ServiceResultConfig.TYPE_AJAX.equals(type)) {
            new AjaxResultHander(request, response).doAjax(data);
            return;
        }
    }

    /**
     * 处理编码问题
     *
     * @param request
     * @param response
     * @zclong
     * @2017年8月5日
     * @ReturnType: void
     */
    public static void setCharacterEncoding(HttpServletRequest request, HttpServletResponse response) {
        try {
            if (null != request) {
                request.setCharacterEncoding(ENCODING);
            }
            if (null != response) {
                response.setCharacterEncoding(ENCODING);
                response.setContentType("text/html;charset="+ENCODING); // 处理响应编码
            }
        } catch (UnsupportedEncodingException e) {
        }
    }

    /**
     * 若请求不存在则转发到404页面
     *
     * @param request
     * @param response
     * @throws IOException
     * @throws ServletException
     * @zclong
     * @2017年8月5日
     * @ReturnType: void
     */
    public static void notfound(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setStatus(HttpServletResponse.SC_NOT_FOUND);
        request.getRequestDispatcher("/WEB-INF/jsps/error/404.jsp").forward(request, response);
    }
}
